`timescale 1ns/1ps
`default_nettype none

/* NOTE:
* - 本模负责将一行中的多个连续像素进行处理并写入到SDRAM
* - 像素数总是8的倍数，一次最多处理256个像素
* - I_write_start拉高后1个周期后开始连续输入像素数据I_write_data
* - O_write_busy拉低前，应保持I_write_len和I_write_addr
*/

module pixel_writer_top (
    // system signal
    input  wire         I_sclk,  // 125M
    input  wire         I_sdram_clk, // 150M
    input  wire         I_rst_n,
    // config
    input  wire [1:0]   I_cfg_scan_mode,
    input  wire [1:0]   I_cfg_box_dir,      // 箱体方向
    input  wire [1:0]   I_cfg_col_loop,     // 列模式读取循环次数(数据组数/8,向上取整)
    input  wire         I_cfg_light_adj_en,  // 全局亮度调整
    input  wire         I_cfg_temp_adj_en,   // 全局色温调整
    input  wire         I_cfg_color_adj_en,  // 全局色度调整
    input  wire         I_cfg_pixel_adj_en,  // 逐点色度调整
    input  wire         I_cfg_gap_adj_en,    // 缝隙调整
    input  wire [15:0]  I_cfg_temp_coe_r,    // 色温调整系数
    input  wire [15:0]  I_cfg_temp_coe_g,    // 色温调整系数
    input  wire [15:0]  I_cfg_temp_coe_b,    // 色温调整系数
    input  wire [15:0]  I_cfg_light_coe,     // 整体亮度系数
    input  wire [15:0]  I_cfg_color_coe_r0,  // 全局色度调整系数
    input  wire [15:0]  I_cfg_color_coe_g0,  // 全局色度调整系数
    input  wire [15:0]  I_cfg_color_coe_b0,  // 全局色度调整系数
    input  wire [15:0]  I_cfg_color_coe_r1,  // 全局色度调整系数
    input  wire [15:0]  I_cfg_color_coe_g1,  // 全局色度调整系数
    input  wire [15:0]  I_cfg_color_coe_b1,  // 全局色度调整系数
    input  wire [15:0]  I_cfg_color_coe_r2,  // 全局色度调整系数
    input  wire [15:0]  I_cfg_color_coe_g2,  // 全局色度调整系数
    input  wire [15:0]  I_cfg_color_coe_b2,  // 全局色度调整系数
    input  wire         I_cfg_contrast_en,   // 对比度使能
    input  wire [7:0]   I_cfg_contrast_coe,  // 对比度系数
    // pixel coe
    output wire         O_pixel_coe_ack, // 当前系数使用完毕，切换到下一个系数
    input  wire [15:0]  I_pixel_coe_r,   // 逐点调整系数
    input  wire [15:0]  I_pixel_coe_g,   // 逐点调整系数
    input  wire [15:0]  I_pixel_coe_b,   // 逐点调整系数
    // gamma
    output wire         O_gamma_start,
    output wire [7:0]   O_gamma_data,
    input  wire [15:0]  I_gamma_result,
    // gap info
    output wire         O_gap_coe_ack,
    input  wire [15:0]  I_gap_coe_val,
    // input pixel
    input  wire         I_write_start,
    output wire         O_write_busy,
    input  wire [20:0]  I_write_addr,
    input  wire [5:0]   I_write_len,
    input  wire [7:0]   I_write_data,
    // sdram mux
    output wire         O_mux_req,
    input  wire         I_mux_ack,
    input  wire         I_mux_irq, // 中断，表示有更高优先级的模块正在请求SDRAM控制权
    output wire         O_mux_cs_n,
    output wire         O_mux_ras_n,
    output wire         O_mux_cas_n,
    output wire         O_mux_we_n,
    output wire [1:0]   O_mux_ba,
    output wire [10:0]  O_mux_addr,
    output wire [31:0]  O_mux_dq_out,
    input  wire [31:0]  I_mux_dq_in,
    output wire         O_mux_dq_oe,
    output wire [3:0]   O_mux_dqm
);
//------------------------Parameter----------------------
// fsm
localparam [2:0]
    IDLE  = 0,
    PIX_R = 1,
    PIX_G = 2,
    PIX_B = 3,
    WAIT  = 4,
    REQ   = 5,
    BUSY  = 6;

//------------------------Local signal-------------------
reg  [2:0]  state;
reg  [2:0]  next;
reg  [8:0]  pixel_cnt;
reg  [9:0]  result_cnt;
wire        pixel_start;
wire        result_valid;
wire [31:0] result_data;
wire        write_req;
wire        write_busy;
reg         next_8pixel;

reg         I_cfg_pixel_adj_en_t;
//------------------------Instantiation------------------
// pixel_process
pixel_process process (/*{{{*/
    .I_sclk             ( I_sclk ),
    .I_rst_n            ( I_rst_n & ~write_req ),
    .I_cfg_light_adj_en ( I_cfg_light_adj_en ),
    .I_cfg_temp_adj_en  ( I_cfg_temp_adj_en ),
    .I_cfg_color_adj_en ( I_cfg_color_adj_en ),
    .I_cfg_pixel_adj_en ( I_cfg_pixel_adj_en_t ),
    .I_cfg_gap_adj_en   ( I_cfg_gap_adj_en ),
    .I_cfg_temp_coe_r   ( I_cfg_temp_coe_r ),
    .I_cfg_temp_coe_g   ( I_cfg_temp_coe_g ),
    .I_cfg_temp_coe_b   ( I_cfg_temp_coe_b ),
    .I_cfg_light_coe    ( I_cfg_light_coe ),
    .I_cfg_color_coe_r0 ( I_cfg_color_coe_r0 ),
    .I_cfg_color_coe_g0 ( I_cfg_color_coe_g0 ),
    .I_cfg_color_coe_b0 ( I_cfg_color_coe_b0 ),
    .I_cfg_color_coe_r1 ( I_cfg_color_coe_r1 ),
    .I_cfg_color_coe_g1 ( I_cfg_color_coe_g1 ),
    .I_cfg_color_coe_b1 ( I_cfg_color_coe_b1 ),
    .I_cfg_color_coe_r2 ( I_cfg_color_coe_r2 ),
    .I_cfg_color_coe_g2 ( I_cfg_color_coe_g2 ),
    .I_cfg_color_coe_b2 ( I_cfg_color_coe_b2 ),
    .I_cfg_contrast_en  ( I_cfg_contrast_en ),
    .I_cfg_contrast_coe ( I_cfg_contrast_coe ),
    .O_pixel_coe_ack    ( O_pixel_coe_ack ),
    .I_pixel_coe_r      ( I_pixel_coe_r ),
    .I_pixel_coe_g      ( I_pixel_coe_g ),
    .I_pixel_coe_b      ( I_pixel_coe_b ),
    .O_gamma_start      ( O_gamma_start ),
    .O_gamma_data       ( O_gamma_data ),
    .I_gamma_result     ( I_gamma_result ),
    .O_gap_coe_ack      ( O_gap_coe_ack ),
    .I_gap_coe_val      ( I_gap_coe_val ),
    .I_pixel_start      ( pixel_start ),
    .I_pixel_data       ( I_write_data ),
    .O_result_valid     ( result_valid ),
    .O_result_data      ( result_data )
);/*}}}*/

// pixel_writer
pixel_writer writer (/*{{{*/
    .I_sclk          ( I_sclk ),
    .I_sdram_clk     ( I_sdram_clk ),
    .I_rst_n         ( I_rst_n ),
    .I_cfg_scan_mode ( I_cfg_scan_mode ),
    .I_cfg_box_dir   ( I_cfg_box_dir ),
    .I_cfg_col_loop  ( I_cfg_col_loop ),
    .I_write_req     ( write_req ),
    .O_write_busy    ( write_busy ),
    .I_write_addr    ( I_write_addr ),
    .I_write_len     ( I_write_len ),
    .I_write_valid   ( result_valid ),
    .I_write_data    ( result_data ),
    .O_mux_req       ( O_mux_req ),
    .I_mux_ack       ( I_mux_ack ),
    .I_mux_irq       ( I_mux_irq ),
    .O_mux_cs_n      ( O_mux_cs_n ),
    .O_mux_ras_n     ( O_mux_ras_n ),
    .O_mux_cas_n     ( O_mux_cas_n ),
    .O_mux_we_n      ( O_mux_we_n ),
    .O_mux_ba        ( O_mux_ba ),
    .O_mux_addr      ( O_mux_addr ),
    .O_mux_dq_out    ( O_mux_dq_out ),
    .I_mux_dq_in     ( I_mux_dq_in ),
    .O_mux_dq_oe     ( O_mux_dq_oe ),
    .O_mux_dqm       ( O_mux_dqm )
);/*}}}*/

//------------------------Body---------------------------
// state
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        state <= IDLE;
    else
        state <= next;
end

// next
always @(*) begin
    case (state)
        IDLE: begin
            if (I_write_start)
                next = PIX_R;
            else
                next = IDLE;
        end

        PIX_R: begin
            next = PIX_G;
        end

        PIX_G: begin
            next = PIX_B;
        end

        PIX_B: begin
            if (pixel_cnt >= {I_write_len, 3'd0})
                next = WAIT;
            else
                next = PIX_R;
        end

        WAIT: begin
            if (result_cnt > {I_write_len, 4'd0})
                next = REQ;
            else
                next = WAIT;
        end

        REQ: begin
            next = BUSY;
        end

        BUSY: begin
            if (write_busy)
                next = BUSY;
            else
                next = IDLE;
        end

        default: begin
            next = IDLE;
        end
    endcase
end

assign O_write_busy = (state != IDLE);
assign pixel_start = (state == IDLE && I_write_start) | next_8pixel;
assign write_req = (state == REQ);

// pixel_cnt
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)begin
        I_cfg_pixel_adj_en_t <= 1'b0;
    end
    else if (write_req)begin
        // I_cfg_pixel_adj_en_t <= I_cfg_pixel_adj_en || I_cfg_gap_adj_en;
        I_cfg_pixel_adj_en_t <= I_cfg_pixel_adj_en;
    end
end

// pixel_cnt
always @(posedge I_sclk) begin
    if (state == IDLE)
        pixel_cnt <= 1'b1;
    else if (state == PIX_B)
        pixel_cnt <= pixel_cnt + 1'b1;
end

// result_cnt
always @(posedge I_sclk) begin
    if (state == IDLE)
        result_cnt <= 1'b1;
    else if (result_valid)
        result_cnt <= result_cnt + 1'b1;
end

// next_8pixel
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        next_8pixel <= 1'b0;
    else if (state == PIX_G) begin
        if (pixel_cnt[8:3] != I_write_len && pixel_cnt[2:0] == 3'd0)
            next_8pixel <= 1'b1;
        else
            next_8pixel <= 1'b0;
    end
    else
        next_8pixel <= 1'b0;
end

endmodule

`default_nettype wire

// vim:set ts=4 sw=4 et fenc=utf-8 fdm=marker:
